package com.doubao.nettyhandler;

import com.doubao.config.AppConfig;
import com.doubao.msghadler.AbstractMsgHandler;
import com.doubao.msghadler.HandlerFactory;
import com.doubao.protocol.Msg;
import com.doubao.protocol.StatusMsg;
import com.doubao.util.ChannelManage;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
import io.netty.handler.codec.http.multipart.InterfaceHttpData;
import io.netty.handler.codec.http.multipart.MemoryAttribute;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.util.CharsetUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * description:WebSocketHandler
 * <p>
 * author:sult
 * time: 2018/11/1 13:18
 **/
public class WebSocketHandler extends SimpleChannelInboundHandler<Object> {


    private static final Logger log = LoggerFactory.getLogger(WebSocketHandler.class);

    // 用于服务器端web套接字打开和关闭握手
    private WebSocketServerHandshaker handshaker;

    private static final String WEB_SOCKET_URL = "/websocket";


    //客户端与服务端创建连接的时候调用
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ChannelManage.group.add(ctx.channel());
        System.out.println("客户端与服务端连接开启，客户端remoteAddress：" + ctx.channel().remoteAddress());
    }

    //客户端与服务端断开连接的时候调用
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        ChannelManage.removeChannel(ctx.channel());
        System.out.println("客户端与服务端连接关闭...");
    }

    //服务端接收客户端发送过来的数据结束之后调用
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

    //工程出现异常的时候调用
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

    //服务端处理客户端websocket请求的核心方法
    @Override
    protected void messageReceived(ChannelHandlerContext context, Object msg) throws Exception {

        if (msg instanceof FullHttpRequest) {
            handHttpRequest(context, (FullHttpRequest) msg);
        } else if (msg instanceof WebSocketFrame) {
            handWebsocketFrame(context, (WebSocketFrame) msg);
        }
    }

    /**
     * 处理客户端与服务端之前的websocket业务
     *
     * @param ctx
     * @param frame
     */
    private void handWebsocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {

        if (frame instanceof CloseWebSocketFrame) {
            handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
        }

        if (frame instanceof PingWebSocketFrame) {
            ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
            return;
        }
        if (frame instanceof TextWebSocketFrame) {
            // 返回应答消息
            String requestMsg = ((TextWebSocketFrame) frame).text();

            Msg msg = Msg.handelMsg(requestMsg);
            if(msg==null){
                ctx.writeAndFlush(new TextWebSocketFrame("消息体转换失败！请见检查报文"));
            }

            if(checkHeartbeat(msg)){
                log.debug("this is heartbeat!");
                return;
            }

            log.info("[server Received msg] {}",requestMsg);
            // 先判断通道管理器中是否存在该通道，没有则添加进去
            channelinit(ctx.channel(), msg);

            HandlerFactory.getMsgHandler(msg,ctx.channel()).sendMsg();
        }
    }

    /**
     * 判断是否是心跳
     *
     * @param msg
     * @return
     */
    private boolean checkHeartbeat(Msg msg) {
        Boolean isHeartbeat=false;
        if (msg instanceof StatusMsg) {
            StatusMsg statusMsg=(StatusMsg)msg;
            if(StatusMsg.MsgStatus.heartbeat.toString().equals(statusMsg.getStatus())) {
                isHeartbeat=true;
            }
        }
        return isHeartbeat;
    }

    private void channelinit(Channel channel, Msg msg) {
        if (!ChannelManage.hasChannel(msg.getFrom())) {
            ChannelManage.initChannel(msg.getFrom(),channel);
        }
    }


    /**
     * 处理客户端向服务端发起http握手请求的业务
     *
     * @param ctx
     * @param req
     */
    private void handHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) {
        System.out.println("处理http请求，http方法==>>" + req.getMethod() + ",http地址==>>" + req.getUri());
        Map<String, String> parmMap = new HashMap<>();
        try {
            parmMap = parse(req);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 如果不是WebSocket握手请求消息，那么就返回 HTTP 400 BAD REQUEST 响应给客户端。
        if (!req.getDecoderResult().isSuccess()
                || !("websocket".equals(req.headers().get("Upgrade")))) {
            sendHttpResponse(ctx, req,
                    new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
            return;
        }
        //如果是握手请求，那么就进行握手
        WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
                AppConfig.websoket_url, null, false);
        handshaker = wsFactory.newHandshaker(req);
        if (handshaker == null) {
            WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());
        } else {
            // 通过它构造握手响应消息返回给客户端，
            // 同时将WebSocket相关的编码和解码类动态添加到ChannelPipeline中，用于WebSocket消息的编解码，
            // 添加WebSocketEncoder和WebSocketDecoder之后，服务端就可以自动对WebSocket消息进行编解码了
            handshaker.handshake(ctx.channel(), req);
        }
    }

    /**
     * 服务端向客户端响应消息
     *
     * @param ctx
     * @param req
     * @param res
     */
    private void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req,
                                  DefaultFullHttpResponse res) {
        // 返回应答给客户端
        if (res.getStatus().code() != 200) {
            ByteBuf buf = Unpooled.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8);
            res.content().writeBytes(buf);
            buf.release();
        }
        // 如果是非Keep-Alive，关闭连接
        ChannelFuture f = ctx.channel().writeAndFlush(res);
        if (res.getStatus().code() != 200) {
            f.addListener(ChannelFutureListener.CLOSE);
        }
    }

    /**
     * 解析GET、POST请求参数
     * @return 包含所有请求参数的键值对, 如果没有参数, 则返回空Map
     *
     * @throws IOException
     */
    public Map<String, String> parse(FullHttpRequest fullReq) throws IOException {

        HttpMethod method = fullReq.getMethod();

        Map<String, String> parmMap = new HashMap<>();

        if (HttpMethod.GET == method) {
            // 是GET请求
            QueryStringDecoder decoder = new QueryStringDecoder(fullReq.getUri());
//            decoder.parameters().entrySet().forEach( entry -> {
//                // entry.getValue()是一个List, 只取第一个元素
//                parmMap.put(entry.getKey(), entry.getValue().get(0));
//            });
//
            //按1.7版本jdk改满足低版本
            for(Map.Entry<String, List<String>> entry:decoder.parameters().entrySet()){
                parmMap.put(entry.getKey(), entry.getValue().get(0));
            }
        } else if (HttpMethod.POST == method) {
            HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(
                    new DefaultHttpDataFactory(false), fullReq);
            List<InterfaceHttpData> postData = decoder.getBodyHttpDatas();
            for(InterfaceHttpData data:postData){
                if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.Attribute) {
                    MemoryAttribute attribute = (MemoryAttribute) data;
                    parmMap.put(attribute.getName(), attribute.getValue());
                }
            }
        } else {
            // 不支持其它方法
            System.out.println("不支持其他方法提交的参数");
        }

        return parmMap;
    }
}
